package cn.iocoder.yudao.module.call.service.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.call.controller.admin.aiqualitytesting.vo.AiQualityTestingSaveReqVO;
import cn.iocoder.yudao.module.call.controller.admin.cmacstaskphone.vo.CmAcsTaskPhonePageReqVO;
import cn.iocoder.yudao.module.call.dal.dataobject.aiqualitytesting.AiQualityTestingDO;
import cn.iocoder.yudao.module.call.dal.dataobject.aiqualitytestingelsecondition.AiQualityTestingElseConditionDO;
import cn.iocoder.yudao.module.call.dal.dataobject.aiqualitytestinglabel.AiQualityTestingLabelDO;
import cn.iocoder.yudao.module.call.dal.dataobject.cmacstask.CmAcsTaskDO;
import cn.iocoder.yudao.module.call.dal.mysql.aiqualitytesting.AiQualityTestingMapper;
import cn.iocoder.yudao.module.call.enums.*;
import cn.iocoder.yudao.module.call.framework.client.CallCenter;
import cn.iocoder.yudao.module.call.framework.client.CallClient;
import cn.iocoder.yudao.module.call.framework.client.CallClientFactory;
import cn.iocoder.yudao.module.call.api.client.dto.ResponseDTO;
import cn.iocoder.yudao.module.call.api.client.dto.task.CreateCallTaskDTO;
import cn.iocoder.yudao.module.call.controller.admin.task.vo.*;
import cn.iocoder.yudao.module.call.dal.dataobject.agent.AgentDO;
import cn.iocoder.yudao.module.call.dal.dataobject.group.GroupDO;
import cn.iocoder.yudao.module.call.dal.dataobject.record.CallRecordDO;
import cn.iocoder.yudao.module.call.dal.dataobject.task.CallTaskDO;
import cn.iocoder.yudao.module.call.dal.dataobject.taskcalleelist.CallTaskCalleeListDO;
import cn.iocoder.yudao.module.call.dal.dataobject.taskcalleelistnumber.CallTaskCalleeListNumberDO;
import cn.iocoder.yudao.module.call.dal.dataobject.tasknumber.TaskNumberDO;
import cn.iocoder.yudao.module.call.dal.mysql.agent.AgentMapper;
import cn.iocoder.yudao.module.call.dal.mysql.group.GroupMapper;
import cn.iocoder.yudao.module.call.dal.mysql.record.CallRecordMapper;
import cn.iocoder.yudao.module.call.dal.mysql.task.CallTaskMapper;
import cn.iocoder.yudao.module.call.dal.mysql.taskcalleelist.CallTaskCalleeListMapper;
import cn.iocoder.yudao.module.call.dal.mysql.taskcalleelistnumber.CallTaskCalleeListNumberMapper;
import cn.iocoder.yudao.module.call.dal.redis.task.TaskRedisKeyConstant;
import cn.iocoder.yudao.module.call.framework.client.impl.cm.CmClient;
import cn.iocoder.yudao.module.call.framework.client.impl.okcc.OkccClient;
import cn.iocoder.yudao.module.call.mq.message.task.CallTaskNumberMessage;
import cn.iocoder.yudao.module.call.mq.producer.task.CallTaskNumberProducer;
import cn.iocoder.yudao.module.call.service.aiqualitytesting.AiQualityTestingService;
import cn.iocoder.yudao.module.call.service.channel.ChannelService;
import cn.iocoder.yudao.module.call.service.cmacstaskphone.CmAcsTaskPhoneService;
import cn.iocoder.yudao.module.call.service.tasknumber.TaskNumberService;
import cn.iocoder.yudao.module.call.utils.MyStringUtil;
import cn.iocoder.yudao.module.crm.api.level.CrmLevelConfigApi;
import cn.iocoder.yudao.module.crm.api.level.dto.CrmLevelConfigDTO;
import cn.iocoder.yudao.module.system.api.extend.ExtendApi;
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
import cn.iocoder.yudao.module.system.enums.product.ProductEnum;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.call.enums.ErrorCodeConstants.*;

/**
 * 外呼任务 Service 实现类
 *
 * @author 数创万维
 */
@Service
@Validated
@Slf4j
public class CallTaskServiceImpl implements CallTaskService {

    @Resource
    private CallTaskMapper taskMapper;

    @Resource
    private CallRecordMapper callRecordMapper;

    @Resource
    private TaskNumberService taskNumberService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private TenantApi tenantApi;

    @Resource
    private CallClientFactory callClientFactory;

    @Resource
    private ChannelService channelService;

    @Resource
    private CmAcsTaskPhoneService cmAcsTaskPhoneService;

    @Resource
    private CallTaskNumberProducer callTaskNumberProducer;

    @Resource
    private CallTaskCalleeListNumberMapper callTaskCalleeListNumberMapper;

    @Resource
    private GroupMapper groupMapper;

    @Resource
    private AgentMapper agentMapper;

    @Resource
    private CallTaskCalleeListMapper callTaskCalleeListMapper;

    @Resource
    private ExtendApi extendApi;

    @Resource
    private AiQualityTestingMapper aiQualityTestingMapper;
    @Resource
    private AiQualityTestingService aiQualityTestingService;

    @Resource
    private CrmLevelConfigApi crmLevelConfigApi;

    private static final String START = "start";
    private static final String STOP = "stop";


    @Override
    public List<CallTaskDO> getTaskList(Collection<String> taskIds) {
        return taskMapper.selectList(new LambdaQueryWrapperX<CallTaskDO>().in(CallTaskDO::getTaskId, taskIds));
    }

    @Override
    public Map<Long, Map<String, CallTaskDO>> getMapByTaskIds(Collection<String> taskIds) {
        if (ObjUtil.isEmpty(taskIds)) {
            return Collections.emptyMap();
        }
        List<CallTaskDO> list = getTaskList(taskIds);
        if (ObjUtil.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.groupingBy(CallTaskDO::getChannelId, Collectors.toMap(CallTaskDO::getTaskId, task -> task)));
    }


    @Override

    public Long createTask(CallTaskSaveReqVO createReqVO) {
        // 插入
        CallTaskDO task = BeanUtils.toBean(createReqVO, CallTaskDO.class);
        // 补充任务初始信息
        fullTaskInfo(task);
        // 调用三方创建任务，返回taskId
        // 查询班组
        CallCenter callClient = (CallCenter) channelService.getTenantCallClient(TenantContextHolder.getTenantId());
        log.info("createTask request: {}", callClient);
        log.info("TenantContextHolder.getTenantId(): {}", TenantContextHolder.getTenantId());
        String thirdPartyGroupId = "0";
        if (!(callClient instanceof CmClient && createReqVO.getType().equals(CmTaskTypeEnum.TO_IVR.getType()))) {
            GroupDO groupDO = groupMapper.selectById(createReqVO.getGroupId());
            if (Objects.isNull(groupDO) || StrUtil.isEmpty(groupDO.getThirdPartyId())) {
                throw exception(GROUP_NOT_EXISTS);
            }
            thirdPartyGroupId = groupDO.getThirdPartyId();
        }
        //记录任务对应外呼渠道Id
        CallClient callClient2 = (CallClient) callClient;
        task.setChannelId(callClient2.getId());

        //查询呼叫名单导入状态,导入中的不可进行操作
        CallTaskCalleeListDO callTaskCalleeListDO = callTaskCalleeListMapper.selectById(createReqVO.getCallTaskCalleeListId());
        // fix a bug : 呼叫名单id查询错误时，会发生空指针问题
        if (Objects.isNull(callTaskCalleeListDO)) {
            throw exception(TASK_CALLEE_LIST_NOT_EXISTS);
        }
        if (callTaskCalleeListDO.getStatus().equals(1)) {
            throw exception(LIST_IMPORTING_ERROR);
        }
        CreateCallTaskDTO data = BeanUtils.toBean(createReqVO, CreateCallTaskDTO.class);
        // 2024年11月19日 补充班组id
        data.setThirdPartyGroupId(thirdPartyGroupId);
        // 调用呼叫名单，获取号码列表，传入到sdk中。
        List<CallTaskCalleeListNumberDO> numberDOList = callTaskCalleeListNumberMapper.selectNumberListByListId(createReqVO.getCallTaskCalleeListId());
        List<String> numberList = numberDOList.stream().map(CallTaskCalleeListNumberDO::getNumber).collect(Collectors.toList());
        int size = numberList.size();
        List<String> calleeList = new ArrayList<>();
        List<String> remainList = new ArrayList<>();
        if (numberList.size() > 1000) {
            calleeList.addAll(numberList.subList(0, 1000));
            remainList.addAll(numberList.subList(999, numberList.size() - 1));
        } else {
            calleeList.addAll(numberList);
        }
        data.setCalleeList(calleeList);
        String cidNumber = tenantApi.getCidGroupNumber(TenantContextHolder.getTenantId());
        data.setCidGroup(cidNumber);
        String thirdPartyTaskId = callClient.createBatchCallTask(data);
        task.setTaskId(thirdPartyTaskId);
        // 补充首次创建就获取号码量
        task.setNumberCount(size);
        if (null == task.getAiQualityTestingId()) {
            LambdaUpdateWrapper<AiQualityTestingDO> wrapper = new LambdaUpdateWrapper<>();
            wrapper.eq(AiQualityTestingDO::getName, "ai质检");
            wrapper.last("limit 1");
            AiQualityTestingDO aiQualityTestingDO = aiQualityTestingMapper.selectOne(wrapper);
            if (null == aiQualityTestingDO) {
                AiQualityTestingSaveReqVO aiQualityTestingSaveReqVO = new AiQualityTestingSaveReqVO();
                aiQualityTestingSaveReqVO.setName("ai质检");

                aiQualityTestingSaveReqVO.setLevelConditionIds("ai质检");
                aiQualityTestingSaveReqVO.setLabelMaxNum(3);
                aiQualityTestingSaveReqVO.setLabelWordNum(4);
                List<CrmLevelConfigDTO> levelConfigList = crmLevelConfigApi.getLevelConfigList();
                List<Long> idList = levelConfigList.stream().map(CrmLevelConfigDTO::getId).collect(Collectors.toList());
                String idString = idList.stream()
                        .map(String::valueOf)  // 将每个 ID 转成字符串
                        .collect(Collectors.joining(","));
                aiQualityTestingSaveReqVO.setLevelConditionIds(idString);
                AiQualityTestingElseConditionDO aiQualityTestingElseConditionDO = new AiQualityTestingElseConditionDO();
                aiQualityTestingElseConditionDO.setType(1).setQuery(1).setValue(20);
                aiQualityTestingSaveReqVO.setAiQualityTestingElseConditionList(Arrays.asList(aiQualityTestingElseConditionDO));
                AiQualityTestingLabelDO aiQualityTestingLabelDOA = new AiQualityTestingLabelDO();
                aiQualityTestingLabelDOA.setContent("客户非常有意向").setFollowUpType("1,5");
                AiQualityTestingLabelDO aiQualityTestingLabelDOB = new AiQualityTestingLabelDO();
                aiQualityTestingLabelDOB.setContent("客户有一点点的意向").setFollowUpType("1");
                AiQualityTestingLabelDO aiQualityTestingLabelDOC = new AiQualityTestingLabelDO();
                aiQualityTestingLabelDOC.setContent("客户没有意向");
                AiQualityTestingLabelDO aiQualityTestingLabelDOD = new AiQualityTestingLabelDO();
                aiQualityTestingLabelDOD.setContent("客户拒绝");
                aiQualityTestingSaveReqVO.setAiQualityTestingLabelList(Arrays.asList(aiQualityTestingLabelDOA, aiQualityTestingLabelDOB, aiQualityTestingLabelDOC, aiQualityTestingLabelDOD));
                Long aiQualityTesting = aiQualityTestingService.createAiQualityTesting(aiQualityTestingSaveReqVO);
                task.setAiQualityTestingId(aiQualityTesting);
            } else {
                task.setAiQualityTestingId(aiQualityTestingDO.getId());
            }
        }
        taskMapper.insert(task);

        // 检查被叫号码，如果超过1k， 需要调用一个异步队列执行号码追加任务；
        if (CollUtil.isNotEmpty(remainList)) {
            callTaskNumberProducer.sendCallTaskNumberMessage(thirdPartyTaskId, remainList, TenantContextHolder.getTenantId());
        }

        // 返回
        return task.getId();
    }

    private void fullTaskInfo(CallTaskDO task) {
        task.setStatus(CallTaskStatusEnum.WAITING.getStatus()).setProgress(0).setNumberCount(0).setCallCount(0).setOwnerUserId(getLoginUserId());
        if (StrUtil.isEmpty(task.getName())) {
            task.setName("群呼任务" + DateUtil.format(new Date(), "yyyyMMdd") + RandomUtil.randomInt(100, 999));
        }
    }

    @Override
    public void appendNumber(String thirdPartyTaskId, String numbers) {
        List<String> numberList = MyStringUtil.split(numbers);
        appendNumber(thirdPartyTaskId, numberList);
    }


    /**
     * 从呼叫名单追加
     */
    @Override
    public void appendNumberByCalleeList(String thirdPartyTaskId, Long callTaskCalleeListId) {
        //查询呼叫名单导入状态,导入中的不可进行操作
        CallTaskCalleeListDO callTaskCalleeListDO = callTaskCalleeListMapper.selectById(callTaskCalleeListId);
        if (callTaskCalleeListDO.getStatus().equals(1)) {
            throw exception(LIST_IMPORTING_ERROR);
        }
        List<CallTaskCalleeListNumberDO> numberDOList = callTaskCalleeListNumberMapper.selectNumberListByListId(callTaskCalleeListId);
        List<String> numberList = numberDOList.stream().map(CallTaskCalleeListNumberDO::getNumber).collect(Collectors.toList());
        appendNumber(thirdPartyTaskId, numberList);
    }

    /**
     * 追加号码
     *
     * @param thirdPartyTaskId
     * @param numberList
     */
    @Override
    public void appendNumber(String thirdPartyTaskId, List<String> numberList) {
        callTaskNumberProducer.sendCallTaskNumberMessage(thirdPartyTaskId, numberList, TenantContextHolder.getTenantId());
    }

    @Override
    @DS("cm")
    public void doAppendNumber(CallTaskNumberMessage message) {

        CallCenter callCenterClient = (CallCenter) channelService.getTenantCallClient(message.getTenantId());
        List<String> calleeList = message.getCalleeList();
        while (true) {
            List<String> numbers = new ArrayList<>();
            if (calleeList.size() > 1000) {
                numbers = calleeList.subList(0, 1000);
            } else {
                numbers = calleeList;
            }
            if (callCenterClient instanceof CmClient) {
                cmAcsTaskPhoneService.appendTaskNumber(message.taskId, numbers);
            } else {
                callCenterClient.appendTaskNumber(message.taskId, numbers);
            }

            if (numbers.size() < 1000) {
                break;
            }
            calleeList = calleeList.subList(999, calleeList.size() - 1);
        }
    }

    @Override
    public void controlTask(CallTaskControlReqVO controlReqVO) {
        CallTaskDO task = getTask(controlReqVO.getId());
        if (Objects.isNull(task)) {
            throw exception(TASK_NOT_EXISTS);
        }
        if (ObjectUtil.equals(controlReqVO.getAction(), START)) {
            // 检查企业群呼话费余额
            Integer balance = tenantApi.getTenantProductBalance(ProductEnum.BATCH_CALL.getCode(), TenantContextHolder.getTenantId());
            if (balance <= 0) {
                throw exception(new ErrorCode(100500, "企业群呼话费余额不足，请充值后重试"));
            }
            handleTaskStart(task);
        } else if (ObjectUtil.equals(controlReqVO.getAction(), STOP)) {

            if (controlReqVO.isRestoreIvrNum()) {
                handleTaskStop(task);
            } else {
                handleTaskStopNotRestore(task);
            }
        }

    }

    @Override
    public CallTaskDO getTaskByTaskIdStr(String taskIdStr) {
        String cacheTaskInfoStr = stringRedisTemplate.opsForValue().get(TaskRedisKeyConstant.CALL_TASK_INFO + taskIdStr);
        if (StrUtil.isNotEmpty(cacheTaskInfoStr)) {
            return JSONUtil.toBean(cacheTaskInfoStr, CallTaskDO.class);
        }
        return taskMapper.selectByTaskId(taskIdStr);
    }

    private void handleTaskStart(CallTaskDO task) {
        // 启动任务
//        startTask(task.getId());
        CallCenter callClient = (CallCenter) channelService.getTenantCallClient(TenantContextHolder.getTenantId());
        if (callClient instanceof CmClient) {
            extendApi.setDomain("");
            extendApi.initMatchCache(task.getIvrFlowId());
            if (task.getType().equals(CmTaskTypeEnum.TO_IVR.getType())) {
                //验证剩余数量是否足够
                if (tenantApi.getTenantProductBalance(ProductEnum.IVR_NUM.getCode(), TenantContextHolder.getTenantId()) < task.getMaxNum()) {
                    throw exception(new ErrorCode(100500, "并发数量不足"));
                }
                //cm的IVR类型任务,扣除占用的并发数
                tenantApi.updateTenantProductBalance(ProductEnum.IVR_NUM.getCode(), TenantContextHolder.getTenantId(), false, task.getMaxNum());
            }
        }
        callClient.callTaskControl(task.getTaskId(), START);
        task.setStatus(CallTaskStatusEnum.RUNNING.getStatus());
        task.setStartTime(LocalDateTime.now());
        taskMapper.updateById(task);
        // 把任务信息也缓存一下，
        stringRedisTemplate.opsForValue().set(TaskRedisKeyConstant.CALL_TASK_INFO + task.getTaskId(), JSONUtil.toJsonStr(task));

    }


    /**
     * 处理任务停止： 删除队列， 修改任务状态
     *
     * @param task CallTaskDO
     * @author PetePower
     * @since 2024-05-20
     */
    private void handleTaskStop(CallTaskDO task) {
        task.setStatus(CallTaskStatusEnum.STOP.getStatus());
        task.setEndTime(LocalDateTime.now());
        // 2. 清空任务信息
        stringRedisTemplate.delete(TaskRedisKeyConstant.CALL_TASK_INFO + task.getTaskId());
        //停止也清除一下flow相关的缓存,兼容一下直接操作库的情况
        extendApi.setDomain("https://127.0.0.1:8082");
        extendApi.clearMatchCache(task.getIvrFlowId());
        CallCenter callClient = (CallCenter) channelService.getTenantCallClient(TenantContextHolder.getTenantId());
        callClient.callTaskControl(task.getTaskId(), STOP);
        if (callClient instanceof CmClient && task.getType().equals(CmTaskTypeEnum.TO_IVR.getType())) {
            CallTaskDO oldTask = getTask(task.getId()); //返还的数量要读取更新之前的
            //cm的转IVR类型任务,返还占用的并发数
            tenantApi.updateTenantProductBalance(ProductEnum.IVR_NUM.getCode(), TenantContextHolder.getTenantId(), true, oldTask.getMaxNum());
        }
        taskMapper.updateById(task);

    }

    /**
     * 停止任务,不返还并发数
     *
     * @param task
     */
    private void handleTaskStopNotRestore(CallTaskDO task) {
        task.setStatus(CallTaskStatusEnum.STOP.getStatus());
        task.setEndTime(LocalDateTime.now());
        taskMapper.updateById(task);
        // 2. 清空任务信息
        stringRedisTemplate.delete(TaskRedisKeyConstant.CALL_TASK_INFO + task.getTaskId());
        CallCenter callClient = (CallCenter) channelService.getTenantCallClient(TenantContextHolder.getTenantId());
        callClient.callTaskControl(task.getTaskId(), STOP);
    }


    @Override
    public void updateTask(CallTaskSaveReqVO updateReqVO) {
        // 校验存在
        CallTaskDO callTaskDO = validateTaskExists(updateReqVO.getId());
        CallCenter callCenter = (CallCenter) channelService.getTenantCallClient(TenantContextHolder.getTenantId());
        // 更新
        CreateCallTaskDTO dto = BeanUtils.toBean(updateReqVO, CreateCallTaskDTO.class);
        CallTaskDO updateObj;
        // 更新
        if (callCenter instanceof OkccClient) {
            //okcc仅可修改一部分字段
            updateObj = BeanUtils.toBean(BeanUtils.toBean(updateReqVO, CallTaskUpdateReqVO.class), CallTaskDO.class);
        } else {
            // 查询班组
            String thirdPartyGroupId = "0";
            if (!(callCenter instanceof CmClient && updateReqVO.getType().equals(CmTaskTypeEnum.TO_IVR.getType()))) {
                GroupDO groupDO = groupMapper.selectById(updateReqVO.getGroupId());
                if (Objects.isNull(groupDO) || StrUtil.isEmpty(groupDO.getThirdPartyId())) {
                    throw exception(GROUP_NOT_EXISTS);
                }
                thirdPartyGroupId = groupDO.getThirdPartyId();
            }
            dto.setThirdPartyGroupId(thirdPartyGroupId);
            updateObj = BeanUtils.toBean(updateReqVO, CallTaskDO.class);

        }
        dto.setTaskId(callTaskDO.getTaskId());

        callCenter.updateBatchTask(dto);
        if (callTaskDO.getStatus().equals(CallTaskStatusEnum.RUNNING.getStatus())) {
            //运行中的才需要停止
            handleTaskStop(callTaskDO);
        }
        taskMapper.updateById(updateObj);

    }

    @Override
    public void simpleUpdateTaskByTaskId(CallTaskDO callTaskDO) {
        taskMapper.update(new LambdaUpdateWrapper<CallTaskDO>()
                .set(CallTaskDO::getStatus, callTaskDO.getStatus())
                .set(CallTaskDO::getNumberCount, callTaskDO.getNumberCount())
                .set(CallTaskDO::getCallCount, callTaskDO.getCallCount())
                .set(CallTaskDO::getProgress, callTaskDO.getProgress())
                .eq(CallTaskDO::getTaskId, callTaskDO.getTaskId()));
    }

    @Override
    public void incCallCount(String taskId) {
        taskMapper.update(new LambdaUpdateWrapper<CallTaskDO>()
                .setSql("call_count = call_count + 1")
                .eq(CallTaskDO::getTaskId, taskId)
        );
    }


    @Override
    public void deleteTask(Long id) {
        // 校验存在同时停止任务
        handleTaskStop(validateTaskExists(id));
        // 删除
        taskMapper.deleteById(id);
        // 2. 清空任务信息
        stringRedisTemplate.delete(TaskRedisKeyConstant.CALL_TASK_INFO + id);
    }

    private CallTaskDO validateTaskExists(Long id) {
        CallTaskDO callTaskDO = taskMapper.selectById(id);
        if (callTaskDO == null) {
            throw exception(TASK_NOT_EXISTS);
        }
        return callTaskDO;
    }

    @Override
    public CallTaskDO getTask(Long id) {
        return taskMapper.selectById(id);
    }

    @Override
    public PageResult<CallTaskDO> getTaskPage(CallTaskPageReqVO pageReqVO) {
        return taskMapper.selectPage(pageReqVO);
    }

    @Override
    public List<CallTaskDO> getTaskList() {
        // 列表默认按状态值排序： 进行中的值给到最大， 把进行中的放前面
        return taskMapper.selectList(new LambdaQueryWrapperX<CallTaskDO>().orderByDesc(CallTaskDO::getCreateTime));
    }

    @Override
    public List<CallTaskDO> getAgentTaskList() {
        AgentDO agent = agentMapper.selectOne(AgentDO::getOwnerUserId, getLoginUserId());
        if (ObjectUtil.isNull(agent)) {
            return Collections.emptyList();
        }
        List<Long> groupIds = new ArrayList<Long>() {{
            if (ObjectUtil.isNotEmpty(agent.getGroupId())) {
                add(agent.getGroupId());
            }
            if (ObjectUtil.isNotEmpty(agent.getSecGroupId())) {
                add(agent.getSecGroupId());
            }
        }};
        if (CollUtil.isEmpty(groupIds)) {
            return Collections.emptyList();
        }
        return taskMapper.selectList(CallTaskDO::getGroupId, groupIds);


    }

    @Override
    public CallTaskNumberRespVO getTaskNumber(String mode, Long taskId) {
        String taskInfo = stringRedisTemplate.opsForValue().get(TaskRedisKeyConstant.CALL_TASK_INFO + taskId);

        CallTaskDO task = JSONUtil.toBean(JSONUtil.parseObj(taskInfo), CallTaskDO.class);

        if (StrUtil.isEmpty(taskInfo)) {
            throw exception(TASK_NOT_EXISTS);
        }
        try {
            checkBillBalance(mode, getLoginUserId());

            if (ObjectUtil.notEqual(task.getStatus(), CallTaskStatusEnum.RUNNING.getStatus())) {
                throw exception(TASK_STATUS_WRONG);
            }

            TaskNumberDO numberInfo = getNextNumberInfo(task);
            if (Objects.isNull(numberInfo)) {
                throw exception(TASK_NUMBER_IS_EMPTY);
            }
//            task.setCallCount(task.getCallCount() + 1);
//            task.setProgress(task.getCallCount() / task.getNumberCount() * 100);
//            stringRedisTemplate.opsForValue().set(TaskRedisKeyConstant.CALL_TASK_INFO + taskId, JSONUtil.toJsonStr(task));
            return CallTaskNumberRespVO.builder().taskInfo(task).numberInfo(numberInfo).build();
        } catch (ServiceException e) {
            task.setStatus(CallTaskStatusEnum.ERROR.getStatus());
            task.setEndTime(LocalDateTime.now());
//            task.setResult(e.getMessage());
            stringRedisTemplate.opsForValue().set(TaskRedisKeyConstant.CALL_TASK_INFO + taskId, JSONUtil.toJsonStr(task));
            taskMapper.updateById(task);
            throw e;
        }

    }

    @Override
    public TaskNumberDO getNextNumberInfo(CallTaskDO task) {
        // 检查号码列表
        String number = stringRedisTemplate.opsForList().leftPop(TaskRedisKeyConstant.CALL_TASK_NUMBER_QUEUE + task.getId());
        if (StrUtil.isEmpty(number)) {
            // 修改任务状态为结束
            task.setStatus(CallTaskStatusEnum.FINISH.getStatus());
            task.setEndTime(LocalDateTime.now());
            taskMapper.updateById(task);
//            throw exception(TASK_NUMBER_IS_EMPTY);
            return null;
        }
//        TaskNumberDO numberInfo = JSONUtil.toBean(JSONUtil.parseObj(number), TaskNumberDO.class);
        return JSONUtil.toBean(JSONUtil.parseObj(number), TaskNumberDO.class);
    }

    @Override
    public void updateTaskNumberStatus(Long id, Integer status, String result) {
        taskNumberService.updateTaskNumber(new TaskNumberDO().setId(id).setStatus(status).setResult(result));
    }

    @Override
    public void stopAllTask() {
        List<CallTaskDO> list = taskMapper.selectList(new LambdaQueryWrapperX<CallTaskDO>().eq(CallTaskDO::getStatus, CallTaskStatusEnum.RUNNING.getStatus()));
        list.forEach(item -> {
            controlTask(new CallTaskControlReqVO().setAction("stop").setId(item.getId()));
        });
    }

    @Override
    public void recallTask(CallTaskRecallReqVO reqVO) {
        // 查询指定任务
        CallTaskDO callTask = taskMapper.selectById(reqVO.getId());
        callTask.setName(reqVO.getName());

        CallCenter callCenter = (CallCenter) channelService.getTenantCallClient(TenantContextHolder.getTenantId());
        CreateCallTaskDTO data = BeanUtils.toBean(callTask, CreateCallTaskDTO.class);
        // 覆盖 任务名称\呼叫倍率
        callTask.setMultiplier(reqVO.getMultiplier());
        GroupDO groupDO = null;
        String thirdPartyId = "0";
        // 重选指定班组
        if (Objects.isNull(reqVO.getGroupId())) {
            groupDO = groupMapper.selectById(callTask.getGroupId());
        } else {
            groupDO = groupMapper.selectById(reqVO.getGroupId());
        }
        if (!Objects.isNull(groupDO)) {
            thirdPartyId = groupDO.getThirdPartyId();
        }
        // 2024年11月19日 补充班组id
        data.setThirdPartyGroupId(thirdPartyId);
        // 获取到主叫分组ID
        String cidNumber = tenantApi.getCidGroupNumber(TenantContextHolder.getTenantId());
        data.setCidGroup(cidNumber);

        // 调用呼叫名单，获取号码列表，传入到sdk中。
        List<CallRecordDO> numberDOList = callRecordMapper.selectList(
                new LambdaQueryWrapperX<CallRecordDO>()
                        .eq(CallRecordDO::getTaskId, callTask.getTaskId())
                        .in(CallRecordDO::getResultType, reqVO.getTypeList())
        );
        if (CollUtil.isEmpty(numberDOList)) {
            throw exception(TASK_NUMBER_IS_EMPTY);
        }
        List<String> numberList = numberDOList.stream().map(CallRecordDO::getCallee).collect(Collectors.toList());
        List<String> calleeList = new ArrayList<>();
        List<String> remainList = new ArrayList<>();
        if (numberList.size() > 1000) {
            calleeList.addAll(numberList.subList(0, 1000));
            remainList.addAll(numberList.subList(999, numberList.size() - 1));
        } else {
            calleeList.addAll(numberList);
        }
        data.setCalleeList(calleeList);

        String thirdPartyTaskId = callCenter.createBatchCallTask(data);
        callTask.setNumberCount(numberList.size());
        callTask.setTaskId(thirdPartyTaskId);
        callTask.setId(null);
        callTask.setCallCount(0);
        callTask.setCreateTime(LocalDateTime.now());
        taskMapper.insert(callTask);

        // 检查被叫号码，如果超过1k， 需要调用一个异步队列执行号码追加任务；
        if (CollUtil.isNotEmpty(remainList)) {
            callTaskNumberProducer.sendCallTaskNumberMessage(thirdPartyTaskId, remainList, TenantContextHolder.getTenantId());
        }
    }

    public void checkBillBalance(String mode, Long id) {
        // 话机盒子不需要走话费查询
        // 如果是sip点呼，校验点呼套餐
        if (PhoneModeEnum.isSIP(mode)) {
            // 查话费
            int phoneBillCount = tenantApi.getTenantProductBalance(ProductEnum.SIMPLE_CALL.getCode(), id);
            if (phoneBillCount <= 0) {
                throw exception(NO_MORE_BILL_BALANCE_ERROR);
            }
        } else if (PhoneModeEnum.isRECALL(mode)) {
            // 查双向回拨套餐
            int phoneBillCount = tenantApi.getTenantProductBalance(ProductEnum.DOUBLE_CALL.getCode(), id);
            if (phoneBillCount <= 0) {
                throw exception(NO_MORE_BILL_BALANCE_ERROR);
            }
        }
    }

    /**
     * okcc定时查询外呼任务的状态\数量信息, 并且更新到本地数据库
     *
     * @author PetePower
     * @since 2024-12-17
     */
//    @Scheduled(fixedRate = 1, timeUnit = TimeUnit.MINUTES)
    @TenantIgnore
    public void autoQueryTaskStatus() {
        List<CallTaskDO> list = taskMapper.selectList(CallTaskDO::getStatus, CallTaskStatusEnum.RUNNING.getStatus());
        OkccClient okccClient = (OkccClient) callClientFactory.getCallClient(1L);
        for (CallTaskDO item : list) {
            syncOkccTask(okccClient, item);

        }
    }

    @Override
    public void syncOkccTask(OkccClient okccClient, CallTaskDO item) {
        try {
            if (StrUtil.isEmpty(item.getTaskId())) {
                return;
            }
            ResponseDTO response = okccClient.queryTaskInfo(item.getTaskId());
            item.setStatus(convertTaskStatus(response.getData().getInt("status")));
            item.setNumberCount(response.getData().getInt("calleeAmount"));
            item.setCallCount(item.getNumberCount() - response.getData().getInt("remainingNumber"));
            item.setProgress(
                    NumberUtil.div(item.getCallCount(), item.getNumberCount()).multiply(new BigDecimal(100)).intValue()
            );
            taskMapper.updateById(item);
        } catch (Exception e) {
            log.error("autoQueryTaskStatus execute error: === {}", e.getMessage());
        }
    }

    @Override
    public void syncCmTask(CmClient cmClient, CallTaskDO callTaskDO) {
        try {
            if (StrUtil.isEmpty(callTaskDO.getTaskId())) {
                return;
            }
            CmAcsTaskDO cmAcsTaskDO = cmClient.queryTaskInfo(callTaskDO.getTaskId());
            callTaskDO.setStatus(CmTaskStatusEnum.getLocalStatusByStatus(cmAcsTaskDO.getStatus()));

            //查询已呼数量
            CmAcsTaskPhonePageReqVO reqVO = new CmAcsTaskPhonePageReqVO();
            reqVO.setAcsTask(NumberUtil.parseLong(callTaskDO.getTaskId()));
            Long numberCount = cmAcsTaskPhoneService.selectCount(reqVO);
            //总量
            callTaskDO.setNumberCount(Math.toIntExact(numberCount));
            reqVO.setStatus(CmTaskPhoneStatusEnum.FINISH.getStatus());
            //完成量
            Long finishCount = cmAcsTaskPhoneService.selectCount(reqVO);
            callTaskDO.setCallCount(Math.toIntExact(finishCount));
            if (finishCount >= numberCount) {
                //已呼数量超出号码总量后判定为终止
                reqVO.setStatus(CmTaskPhoneStatusEnum.FINISH.getStatus());
            }
            //Ivr类型的任务如果为停止,将占用的并发数返还
            if (callTaskDO.getType().equals(CmTaskTypeEnum.TO_IVR.getType()) && CmTaskStatusEnum.isStopStatusByLocalStatus(callTaskDO.getStatus())) {
                tenantApi.updateTenantProductBalance(ProductEnum.IVR_NUM.getCode(), cmAcsTaskDO.getTenantId(), true, callTaskDO.getMaxNum());
            }

            taskMapper.updateById(callTaskDO);
        } catch (Exception e) {
            log.error("autoQueryTaskStatus execute error: === {}", e.getMessage());
        }
    }

    private Integer convertTaskStatus(Integer status) {
        if (status == 0) {
            return CallTaskStatusEnum.WAITING.getStatus();
        }
        if (status == 1) {
            return CallTaskStatusEnum.RUNNING.getStatus();
        }
        if (status == 2) {
            return CallTaskStatusEnum.PAUSE.getStatus();
        }
        if (status == 3) {
            return CallTaskStatusEnum.STOP.getStatus();
        }
        return CallTaskStatusEnum.ERROR.getStatus();
    }


}